package ccm.communicate.serial;

import ccm.common.Configurable;
import ccm.common.ReceiveDataEvent;
import ccm.common.StatusChangeEvent;
import ccm.communicate.common.Communicate;
import ccm.communicate.common.NotStartException;
import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;

import javax.swing.*;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class Serial extends Communicate implements SerialPortDataListener, Configurable
{
    /**
     * 保存串口号
     */
    protected SerialPort serialPort;
    /**
     * 默认波特率
     */
    protected int        baudRate=460800;
    /**
     * 默认数据位
     */
    protected int        dataBits=8;
    /**
     * 默认停止位
     */
    protected int        stopBits=SerialPort.ONE_STOP_BIT;
    /**
     * 默认校验位
     */
    protected int        parity  =SerialPort.NO_PARITY;

    public Serial()
    {
        super();
        new Thread(()->{
            try
            {
                while(true)
                {
                    if(serialPort!=null&&serialPort.getLastErrorCode()!=0)
                    {
                        stop();
                    }
                    Thread.sleep(1000);
                }
            }catch(InterruptedException e)
            {
                e.printStackTrace();
            }
        }).start();
    }

    /**
     * 启动串口
     */
    public void start()
    {
        if(serialPort!=null&&!serialPort.isOpen())
        {
            serialPort.openPort();
            serialPort.setComPortParameters(baudRate,dataBits,stopBits,parity);
            statusChange(StatusChangeEvent.START_STOP);
        }
    }

    /**
     * 停止串口
     */
    public void stop()
    {
        if(serialPort!=null&&serialPort.isOpen())
        {
            serialPort.closePort();
            statusChange(StatusChangeEvent.START_STOP);
        }
    }

    public boolean isOpen()
    {
        return serialPort!=null&&serialPort.isOpen();
    }

    /**
     * 发送数据
     *
     * @param data    数据
     * @param channel 通道
     * @throws NotStartException 未启动时抛出此异常
     * @implNote 通道未使用
     */
    public void send(byte[] data,int channel) throws NotStartException
    {
        if(serialPort==null||!serialPort.isOpen())
            throw new NotStartException();
        serialPort.writeBytes(data,data.length);
    }

    /**
     * 用于保存参数
     */
    public void writeExternal(ObjectOutput out) throws IOException
    {
        out.writeObject(getSerialPort()==null?null:getSerialPort().getSystemPortName());
        out.writeInt(getBaudRate());
        out.writeInt(getDataBits());
        out.writeInt(getStopBits());
        out.writeInt(getParity());
    }

    /**
     * 用于加载参数
     */
    public void readExternal(ObjectInput in) throws IOException
    {
        try
        {
            String str=(String)in.readObject();
            for(SerialPort port: SerialPort.getCommPorts())
                if(port.getSystemPortName().equals(str))
                    setSerialPort(port);
        }catch(Exception e)
        {
            e.printStackTrace();
        }
        setBaudRate(in.readInt());
        setDataBits(in.readInt());
        setStopBits(in.readInt());
        setParity(in.readInt());
    }

    /**
     * 获取串口号
     *
     * @return 串口号
     */
    public SerialPort getSerialPort()
    {
        return serialPort;
    }

    /**
     * 设置串口号
     *
     * @param serialPort 串口号
     */
    public void setSerialPort(SerialPort serialPort)
    {
        stop();
        this.serialPort=serialPort;
        if(this.serialPort!=null)
        {
            this.serialPort.addDataListener(this);
        }
        statusChange(StatusChangeEvent.CONFIG);
    }

    /**
     * 获取波特率
     *
     * @return 波特率
     */
    public int getBaudRate()
    {
        return baudRate;
    }

    /**
     * 设置波特率
     *
     * @param baudRate 波特率
     */
    public void setBaudRate(int baudRate)
    {
        if(serialPort!=null)
            serialPort.setBaudRate(this.baudRate=baudRate);
        statusChange(StatusChangeEvent.CONFIG);
    }

    /**
     * 获取数据位
     *
     * @return 数据位
     */
    public int getDataBits()
    {
        return dataBits;
    }

    /**
     * 设置数据位
     *
     * @param dataBits 数据位
     */
    public void setDataBits(int dataBits)
    {
        if(serialPort!=null)
            serialPort.setNumDataBits(this.dataBits=dataBits);
        statusChange(StatusChangeEvent.CONFIG);
    }

    /**
     * 获取停止位
     *
     * @return 停止位
     */
    public int getStopBits()
    {
        return stopBits;
    }

    /**
     * 设置停止位
     *
     * @param stopBits 停止位
     */
    public void setStopBits(int stopBits)
    {
        if(serialPort!=null)
            serialPort.setNumStopBits(this.stopBits=stopBits);
        statusChange(StatusChangeEvent.CONFIG);
    }

    /**
     * 获取校验位
     *
     * @return 校验位
     */
    public int getParity()
    {
        return parity;
    }

    /**
     * 设置校验位
     *
     * @param parity 校验
     */
    public void setParity(int parity)
    {
        if(serialPort!=null)
            serialPort.setParity(this.parity=parity);
        statusChange(StatusChangeEvent.CONFIG);
    }

    /**
     * SerialPortDataListener实现
     */
    public int getListeningEvents()
    {
        return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
    }

    /**
     * SerialPortDataListener实现
     * 当串口接收到数据时触发
     */
    public void serialEvent(SerialPortEvent event)
    {
        if(event.getEventType()==SerialPort.LISTENING_EVENT_DATA_AVAILABLE)
        {
            byte[] newData=new byte[serialPort.bytesAvailable()];
            serialPort.readBytes(newData,newData.length);
            ReceiveDataEvent e=new ReceiveDataEvent(this,newData);
            for(int i=0;i<receiveDataListener.length;++i)
                callReceiveDataListener(i,e);
        }
    }

    /**
     * 获取配置用的面板
     *
     * @return 配置用的面板
     */
    public JPanel getConfigJPanel()
    {
        return new SerialConfigJPanel(this);
    }
}
